﻿namespace JoinBox.MathEx.TSP;

using JoinBox.MathEx;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// 旅行路线
/// </summary>
public class Tour
{
    #region 成员
    /// <summary>
    /// 城市集合
    /// </summary>
    public List<City> Citys { get; private set; }
    /// <summary>
    /// 总距离
    /// </summary>
    public double Distance { get; private set; }
    /// <summary>
    /// 适应度
    /// </summary>
    public double Fitness { get; private set; }
    #endregion

    #region 构造
    /// <summary>
    /// 旅行路线
    /// </summary>
    /// <param name="citys">前往的城市集合</param>
    public Tour(List<City> citys)
    {
        Citys    = citys;
        Distance = CalcDist();
        Fitness  = 1 / Distance;
    }

    /// <summary>
    /// 根据随机点生成城市,用静态函数进入构造函数
    /// </summary>
    /// <param name="n">生成几个</param>
    /// <returns></returns>
    public static Tour BuildRandom(int n)
    {
        List<City> citys = new();
        for (int i = 0; i < n; ++i)
            citys.Add(City.Random(GeneticInfoBasal.Random));

        return new Tour(citys);
    }

    /// <summary>
    /// 根据点集生成城市,用静态函数进入构造函数
    /// </summary>
    /// <param name="pts">点集</param>
    /// <returns></returns>
    public static Tour Build(List<PointV> pts)
    {
        List<City> citys = new();
        pts.ForEach(pt => citys.Add(new City(pt)));
        return new Tour(citys);
    }
    #endregion

    #region 方法
    /// <summary>
    /// 计算行走路线的距离
    /// </summary>
    /// <returns></returns>
    double CalcDist()
    {
        double total = 0;
        for (int i = 0; i < Citys.Count; ++i)
        {
            // 取余是遍历到[Count-1]时和[0]对比长度,形成首尾相连
            total += Citys[i].DistanceTo(Citys[(i + 1) % Citys.Count]);
        }
        return total;

        // linq执行时间翻了一倍
        // return this.t.Sum( c => c.DistanceTo(this.t[ (this.t.IndexOf(c) + 1) % this.t.Count] ) );
    }

    /// <summary>
    /// 线路打散
    /// </summary>
    /// <returns></returns>
    public Tour Shuffle()
    {
        var citys = new List<City>(Citys);
        int n = citys.Count;

        while (n > 1)
        {
            n--;
            int k = GeneticInfoBasal.Random.Next(n + 1);
#pragma warning disable IDE0180 // 使用元组交换值
            var v = citys[k];
            citys[k] = citys[n];
            citys[n] = v;
#pragma warning restore IDE0180 // 使用元组交换值
        }
        return new Tour(citys);
    }

    /// <summary>
    /// 交叉(交配生子)
    /// </summary>
    public Tour Crossover(Tour b)
    {
        int n1 = GeneticInfoBasal.Random.Next(0, b.Citys.Count);
        int n2 = GeneticInfoBasal.Random.Next(n1, b.Citys.Count);// 不可以和自己交配,所以n1
        List<City> s = Citys.GetRange(n1, n2 - n1 + 1);
        List<City> ms = b.Citys.Except(s).ToList();
        List<City> c = ms.Take(n1)
                         .Concat(s)
                         .Concat(ms.Skip(n1))
                         .ToList();
        return new Tour(c);
    }

    /// <summary>
    /// 变异
    /// </summary>
    /// <param name="mutateProbability">变异概率</param>
    /// <returns></returns>
    public Tour Mutate(double mutateProbability)
    {
        var citys = new List<City>(Citys);
        for (int i = 0; i < citys.Count; i++)
        {
            if (GeneticInfoBasal.Random.NextDouble() < mutateProbability)
            {
                int n1    = GeneticInfoBasal.Random.Next(0, citys.Count);
                int n2    = GeneticInfoBasal.Random.Next(0, citys.Count);// 可以自我变异,所以0
#pragma warning disable IDE0180 // 使用元组交换值
                var v    = citys[n1];
                citys[n1] = citys[n2];
                citys[n2] = v;
#pragma warning restore IDE0180 // 使用元组交换值
            }
        }
        return new Tour(citys);
    }
    #endregion
}